#!/bin/bash

###############################################################################
# 企业级前端项目手动部署脚本
# 项目：vite-gitlab-cicd-demo
# 用途：当 CI/CD 失败时，使用此脚本进行手动部署
# 版本：1.0.0
# 作者：DevOps Team
###############################################################################

set -e  # 遇到错误立即退出
# set -x  # 调试时取消注释，打印每条命令

###############################################################################
# 颜色输出
###############################################################################
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

###############################################################################
# 配置变量（请根据实际情况修改）
###############################################################################

# 项目配置
PROJECT_NAME="vite-gitlab-cicd-demo"
GIT_REPO="https://gitlab.com/your-group/vite-gitlab-cicd-demo.git"
DOCKER_REGISTRY="registry.gitlab.com"
DOCKER_IMAGE_PATH="your-group/vite-gitlab-cicd-demo"

# 测试环境配置
TEST_SERVER_HOST="test-server.company.com"
TEST_SERVER_USER="deploy"
TEST_SERVER_PORT="8081"

# 生产环境配置
PROD_SERVER_HOST="prod-server.company.com"
PROD_SERVER_USER="deploy"
PROD_SERVER_PORT="80"

# 本地工作目录
WORK_DIR="/tmp/deploy-${PROJECT_NAME}"

###############################################################################
# 显示帮助信息
###############################################################################
show_help() {
    cat << EOF
企业级前端项目手动部署脚本

用法：
    $0 [命令] [选项]

命令：
    build-docker        构建 Docker 镜像（本地）
    deploy-test         部署到测试环境
    deploy-prod         部署到生产环境
    build-static        构建静态文件
    deploy-static-test  部署静态文件到测试环境
    deploy-static-prod  部署静态文件到生产环境
    rollback-test       回滚测试环境到上一个版本
    rollback-prod       回滚生产环境到上一个版本
    help                显示帮助信息

选项：
    -v, --version       指定部署版本（Git Tag，例如：v1.2.3）
    -b, --branch        指定分支（例如：develop）
    -c, --commit        指定 Commit Hash
    -e, --env           指定环境（test/prod，默认：test）

示例：
    # 构建 Docker 镜像（生产环境，版本 v1.2.3）
    $0 build-docker --version v1.2.3 --env prod

    # 部署到测试环境
    $0 deploy-test --branch develop

    # 部署到生产环境
    $0 deploy-prod --version v1.2.3

    # 构建静态文件并部署到测试环境
    $0 build-static --branch develop
    $0 deploy-static-test

    # 回滚生产环境
    $0 rollback-prod

EOF
}

###############################################################################
# 参数解析
###############################################################################
parse_args() {
    COMMAND=""
    VERSION=""
    BRANCH=""
    COMMIT=""
    ENVIRONMENT="test"

    # 第一个参数是命令
    if [[ $# -gt 0 ]]; then
        COMMAND=$1
        shift
    fi

    # 解析选项
    while [[ $# -gt 0 ]]; do
        case $1 in
            -v|--version)
                VERSION="$2"
                shift 2
                ;;
            -b|--branch)
                BRANCH="$2"
                shift 2
                ;;
            -c|--commit)
                COMMIT="$2"
                shift 2
                ;;
            -e|--env)
                ENVIRONMENT="$2"
                shift 2
                ;;
            *)
                log_error "未知参数: $1"
                show_help
                exit 1
                ;;
        esac
    done
}

###############################################################################
# 环境检查
###############################################################################
check_dependencies() {
    log_info "检查依赖工具..."

    local missing_tools=()

    # 检查 Git
    if ! command -v git &> /dev/null; then
        missing_tools+=("git")
    fi

    # 检查 Docker（如果使用 Docker 部署）
    if [[ "$COMMAND" == build-docker* ]] || [[ "$COMMAND" == deploy-* ]]; then
        if ! command -v docker &> /dev/null; then
            missing_tools+=("docker")
        fi
    fi

    # 检查 Node.js 和 pnpm（如果构建静态文件）
    if [[ "$COMMAND" == build-static* ]]; then
        if ! command -v node &> /dev/null; then
            missing_tools+=("node")
        fi
        if ! command -v pnpm &> /dev/null; then
            log_warning "未找到 pnpm，将使用 npm"
        fi
    fi

    # 检查 SSH（如果部署到远程服务器）
    if [[ "$COMMAND" == deploy-* ]]; then
        if ! command -v ssh &> /dev/null; then
            missing_tools+=("ssh")
        fi
    fi

    if [[ ${#missing_tools[@]} -gt 0 ]]; then
        log_error "缺少以下依赖工具：${missing_tools[*]}"
        log_error "请先安装这些工具后再运行脚本"
        exit 1
    fi

    log_success "依赖检查通过"
}

###############################################################################
# 准备工作目录
###############################################################################
prepare_workspace() {
    log_info "准备工作目录：$WORK_DIR"

    # 创建工作目录
    mkdir -p "$WORK_DIR"
    cd "$WORK_DIR"

    # 如果目录已存在，询问是否清理
    if [[ -d "$PROJECT_NAME" ]]; then
        log_warning "工作目录已存在，正在清理..."
        rm -rf "$PROJECT_NAME"
    fi

    log_success "工作目录准备完成"
}

###############################################################################
# 克隆代码
###############################################################################
clone_code() {
    log_info "克隆代码仓库..."

    cd "$WORK_DIR"
    git clone "$GIT_REPO" "$PROJECT_NAME"
    cd "$PROJECT_NAME"

    # 根据参数切换版本
    if [[ -n "$VERSION" ]]; then
        log_info "切换到版本：$VERSION"
        git fetch --all --tags
        git checkout "tags/$VERSION"
    elif [[ -n "$BRANCH" ]]; then
        log_info "切换到分支：$BRANCH"
        git checkout "$BRANCH"
        git pull origin "$BRANCH"
    elif [[ -n "$COMMIT" ]]; then
        log_info "切换到 Commit：$COMMIT"
        git checkout "$COMMIT"
    else
        log_error "必须指定版本（--version）、分支（--branch）或 Commit（--commit）"
        exit 1
    fi

    log_success "代码克隆完成"
}

###############################################################################
# 构建 Docker 镜像
###############################################################################
build_docker_image() {
    log_info "开始构建 Docker 镜像（环境：$ENVIRONMENT）..."

    cd "$WORK_DIR/$PROJECT_NAME"

    # 确定构建环境和标签
    local build_env
    local image_tag
    local image_name

    if [[ "$ENVIRONMENT" == "prod" ]]; then
        build_env="production"
        if [[ -n "$VERSION" ]]; then
            image_tag="$VERSION"
        else
            image_tag="prod-latest"
        fi
    else
        build_env="test"
        image_tag="test-latest"
    fi

    image_name="${PROJECT_NAME}:${image_tag}"

    log_info "构建环境：$build_env"
    log_info "镜像名称：$image_name"

    # 构建镜像
    docker build \
        --build-arg BUILD_ENV="$build_env" \
        -t "$image_name" \
        .

    log_success "Docker 镜像构建完成：$image_name"

    # 打标签用于推送到 Registry
    local registry_image="${DOCKER_REGISTRY}/${DOCKER_IMAGE_PATH}:${image_tag}"
    docker tag "$image_name" "$registry_image"

    log_info "推送镜像到 Registry：$registry_image"

    # 登录 Docker Registry（需要提前配置）
    # docker login "$DOCKER_REGISTRY"

    docker push "$registry_image"

    log_success "镜像推送完成：$registry_image"

    # 保存镜像信息供后续使用
    echo "$registry_image" > "$WORK_DIR/last_image.txt"
}

###############################################################################
# 构建静态文件
###############################################################################
build_static_files() {
    log_info "开始构建静态文件（环境：$ENVIRONMENT）..."

    cd "$WORK_DIR/$PROJECT_NAME"

    # 安装依赖
    log_info "安装依赖..."
    if command -v pnpm &> /dev/null; then
        pnpm install --frozen-lockfile
    else
        npm install
    fi

    # 构建项目
    log_info "构建项目..."
    if [[ "$ENVIRONMENT" == "prod" ]]; then
        if command -v pnpm &> /dev/null; then
            pnpm run build:prod
        else
            npm run build:prod
        fi
    else
        if command -v pnpm &> /dev/null; then
            pnpm run build:test
        else
            npm run build:test
        fi
    fi

    # 打包构建产物
    log_info "打包构建产物..."
    cd dist
    local timestamp=$(date +%Y%m%d-%H%M%S)
    local archive_name="${PROJECT_NAME}-${ENVIRONMENT}-${timestamp}.tar.gz"
    tar -czf "$WORK_DIR/$archive_name" *
    cd ..

    log_success "构建产物已打包：$WORK_DIR/$archive_name"

    # 保存文件名供后续使用
    echo "$archive_name" > "$WORK_DIR/last_build.txt"
}

###############################################################################
# 部署 Docker 到远程服务器
###############################################################################
deploy_docker_to_server() {
    local server_host=$1
    local server_user=$2
    local server_port=$3
    local environment=$4

    log_info "部署到服务器：$server_user@$server_host"

    # 读取上次构建的镜像名称
    if [[ ! -f "$WORK_DIR/last_image.txt" ]]; then
        log_error "未找到构建的镜像信息，请先执行 build-docker"
        exit 1
    fi

    local image_name=$(cat "$WORK_DIR/last_image.txt")
    local container_name="${PROJECT_NAME}-${environment}"

    log_info "部署镜像：$image_name"
    log_info "容器名称：$container_name"

    # 远程部署脚本
    local deploy_script=$(cat <<EOF
#!/bin/bash
set -e

echo "【1/6】登录 Docker Registry..."
# docker login $DOCKER_REGISTRY  # 需要配置凭据

echo "【2/6】拉取最新镜像..."
docker pull $image_name

echo "【3/6】备份当前容器..."
if docker ps -a | grep -q $container_name; then
    docker commit $container_name ${container_name}-backup-\$(date +%Y%m%d-%H%M%S) || true
fi

echo "【4/6】停止并删除旧容器..."
docker stop $container_name || true
docker rm $container_name || true

echo "【5/6】启动新容器..."
docker run -d \\
    --name $container_name \\
    --restart always \\
    -p $server_port:80 \\
    -v /data/nginx/logs:/var/log/nginx \\
    $image_name

echo "【6/6】健康检查..."
sleep 5
docker ps | grep $container_name
curl -f http://localhost:$server_port/health.txt || {
    echo "健康检查失败！"
    exit 1
}

echo "✅ 部署成功！"
docker logs --tail 20 $container_name
EOF
)

    # 执行远程部署
    log_info "执行远程部署..."
    ssh -t "$server_user@$server_host" "$deploy_script"

    log_success "部署完成！访问地址：http://$server_host:$server_port"
}

###############################################################################
# 部署静态文件到远程服务器
###############################################################################
deploy_static_to_server() {
    local server_host=$1
    local server_user=$2
    local environment=$3

    log_info "部署静态文件到服务器：$server_user@$server_host"

    # 读取上次构建的文件名
    if [[ ! -f "$WORK_DIR/last_build.txt" ]]; then
        log_error "未找到构建产物，请先执行 build-static"
        exit 1
    fi

    local archive_name=$(cat "$WORK_DIR/last_build.txt")
    local archive_path="$WORK_DIR/$archive_name"

    if [[ ! -f "$archive_path" ]]; then
        log_error "构建产物文件不存在：$archive_path"
        exit 1
    fi

    log_info "上传文件：$archive_name"

    # 上传文件到服务器
    scp "$archive_path" "$server_user@$server_host:/tmp/"

    # 远程部署脚本
    local deploy_script=$(cat <<EOF
#!/bin/bash
set -e

echo "【1/5】备份当前版本..."
sudo mkdir -p /backup
if [ -d /var/www/$PROJECT_NAME/current ]; then
    sudo tar -czf /backup/${PROJECT_NAME}-backup-\$(date +%Y%m%d-%H%M%S).tar.gz -C /var/www/$PROJECT_NAME/current .
fi

echo "【2/5】创建新版本目录..."
version_dir="/var/www/$PROJECT_NAME/releases/\$(date +%Y%m%d-%H%M%S)"
sudo mkdir -p "\$version_dir"

echo "【3/5】解压新版本..."
sudo tar -xzf /tmp/$archive_name -C "\$version_dir"

echo "【4/5】更新软链接..."
sudo ln -snf "\$version_dir" /var/www/$PROJECT_NAME/current

echo "【5/5】重载 Nginx..."
sudo nginx -t
sudo nginx -s reload

echo "✅ 部署成功！"
ls -la /var/www/$PROJECT_NAME/current

# 清理临时文件
rm -f /tmp/$archive_name

# 清理旧版本（保留最近 5 个）
cd /var/www/$PROJECT_NAME/releases
ls -t | tail -n +6 | xargs -r sudo rm -rf
EOF
)

    # 执行远程部署
    log_info "执行远程部署..."
    ssh -t "$server_user@$server_host" "$deploy_script"

    log_success "部署完成！"
}

###############################################################################
# 回滚操作
###############################################################################
rollback_deployment() {
    local server_host=$1
    local server_user=$2
    local environment=$3

    log_warning "开始回滚 $environment 环境..."

    # 远程回滚脚本
    local rollback_script=$(cat <<EOF
#!/bin/bash
set -e

echo "查看可用的备份版本..."
docker images | grep ${PROJECT_NAME}-${environment}-backup || {
    echo "未找到备份镜像"

    # 如果没有镜像备份，尝试回滚静态文件
    if [ -d /var/www/$PROJECT_NAME/releases ]; then
        echo "可用的历史版本："
        ls -lt /var/www/$PROJECT_NAME/releases | head -n 6

        echo "请输入要回滚到的版本目录名（或按 Ctrl+C 取消）："
        read version_dir

        if [ -d "/var/www/$PROJECT_NAME/releases/\$version_dir" ]; then
            sudo ln -snf "/var/www/$PROJECT_NAME/releases/\$version_dir" /var/www/$PROJECT_NAME/current
            sudo nginx -s reload
            echo "✅ 回滚成功！"
        else
            echo "版本目录不存在"
            exit 1
        fi
    else
        echo "未找到可回滚的版本"
        exit 1
    fi
    exit 0
}

# 使用最近的备份镜像回滚
latest_backup=\$(docker images | grep ${PROJECT_NAME}-${environment}-backup | head -n 1 | awk '{print \$1":"\$2}')

if [ -z "\$latest_backup" ]; then
    echo "未找到备份镜像"
    exit 1
fi

echo "回滚到备份镜像：\$latest_backup"

docker stop ${PROJECT_NAME}-${environment} || true
docker rm ${PROJECT_NAME}-${environment} || true

docker run -d \\
    --name ${PROJECT_NAME}-${environment} \\
    --restart always \\
    -p $server_port:80 \\
    "\$latest_backup"

echo "✅ 回滚成功！"
docker logs --tail 20 ${PROJECT_NAME}-${environment}
EOF
)

    # 执行远程回滚
    ssh -t "$server_user@$server_host" "$rollback_script"

    log_success "回滚完成！"
}

###############################################################################
# 主流程控制
###############################################################################
main() {
    # 解析参数
    parse_args "$@"

    # 显示帮助
    if [[ "$COMMAND" == "help" ]] || [[ -z "$COMMAND" ]]; then
        show_help
        exit 0
    fi

    # 显示执行信息
    log_info "========================================="
    log_info "项目：$PROJECT_NAME"
    log_info "命令：$COMMAND"
    log_info "环境：$ENVIRONMENT"
    [[ -n "$VERSION" ]] && log_info "版本：$VERSION"
    [[ -n "$BRANCH" ]] && log_info "分支：$BRANCH"
    [[ -n "$COMMIT" ]] && log_info "Commit：$COMMIT"
    log_info "========================================="

    # 执行命令
    case "$COMMAND" in
        build-docker)
            check_dependencies
            prepare_workspace
            clone_code
            build_docker_image
            ;;

        deploy-test)
            check_dependencies
            prepare_workspace
            clone_code
            ENVIRONMENT="test"
            build_docker_image
            deploy_docker_to_server "$TEST_SERVER_HOST" "$TEST_SERVER_USER" "$TEST_SERVER_PORT" "test"
            ;;

        deploy-prod)
            check_dependencies
            prepare_workspace
            clone_code
            ENVIRONMENT="prod"
            build_docker_image
            deploy_docker_to_server "$PROD_SERVER_HOST" "$PROD_SERVER_USER" "$PROD_SERVER_PORT" "prod"
            ;;

        build-static)
            check_dependencies
            prepare_workspace
            clone_code
            build_static_files
            ;;

        deploy-static-test)
            check_dependencies
            ENVIRONMENT="test"
            deploy_static_to_server "$TEST_SERVER_HOST" "$TEST_SERVER_USER" "test"
            ;;

        deploy-static-prod)
            check_dependencies
            ENVIRONMENT="prod"
            deploy_static_to_server "$PROD_SERVER_HOST" "$PROD_SERVER_USER" "prod"
            ;;

        rollback-test)
            check_dependencies
            rollback_deployment "$TEST_SERVER_HOST" "$TEST_SERVER_USER" "test"
            ;;

        rollback-prod)
            check_dependencies
            rollback_deployment "$PROD_SERVER_HOST" "$PROD_SERVER_USER" "prod"
            ;;

        *)
            log_error "未知命令：$COMMAND"
            show_help
            exit 1
            ;;
    esac

    log_success "========================================="
    log_success "所有操作完成！"
    log_success "========================================="
}

# 执行主流程
main "$@"

